package QianFeng08.CollectionDemo;

import java.util.*;

/**
 * Map接口：
 * public interface Map<K,V>
 * 将键映射到值的对象，一个映射不能包含重复的键;每个键最多只能映射到一个值。
 * 1、键值对存储一组对象
 * 2、Key不能重复（唯一），value可以重复
 * 3、具体的实现类：HashMap、TreeMap、Hashtable、LinkedHashMap
 * 4、HashMap与Hashtable的区别？
 * 5、如何选择使用哪个？
 *
 * void clear()                          清空Map集合中的内容
 * boolean containsKey(Object key)       判断集合中是否存在指定的key
 * boolean containsValue(Object value)   判断集合中是否存在指定的value
 * Set<Map.Entry<K,V>> entrySet()        将Map接口变为Set集合
 * V get(Object key)                     根据key找到其对应的value
 **/
public class MapDemo {
    public static void main(String[] args) {
        //hashMap();
        //hashtable();
        treeMap();
    }

    /**
     * HashMap
     * public class HashMap<K,V> extends AbstractMap<K,V>
     * implements Map<K,V>, Cloneable, Serializable
     * 基于哈希表（数组+链表+二叉树）的Map接口的实现。此实现提供所有可选的映射操作，并允许使用null值和null键。
     * (除了非同步和允许使用null之外，HashMap类与Hashtable大致相同。)
     * 此类不保证映射的顺序，特别是它不保证该顺序恒久不变。
     *
     * 1、默认的加载因子为0.75提供了一个很好的时间和空间成本之间的权衡。较高的值会降低空间开销，但提高查找成本
     * 2、默认数组大小是16
     * 3、把对象存储到哈希表中，如何存储？
     *      把Key对象通过hash()方法计算hash值，然后用这个hash值对数组长度取余数（默认16），
     *      来决定该Key对象在数组中存储的位置，当这个位置有多个对象时，以链表结构存储
     *      JDK1.8后，当链表长度大于8时，链表将转换为二叉树结构存储
     *      这样的目的，是为了取值更快，存储的数据量越大，性能的表现越明显
     * 4、数组扩充原理：当数组的容量超过了75%，那么表示该数组需要扩充，如何扩充？
     *      扩充的算法是：当前数组的容量<<1 相当于×2 ，扩大一倍，扩充次数过多会影响性能，
     *      每次扩充表示哈希表重新散列（重新计算每个对象的存储位置），我们开发中尽量减少扩充次数
     * 5、线程不安全，适合单线程中使用
     **/
    private static void hashMap() {
        Map<Integer, String> map = new HashMap<>();
        map.put(1, "大神");
        map.put(2, "大佬");
        map.put(3, "高手");
        map.put(4, "菜鸟");
        //元素的个数
        System.out.println("size=" + map.size());
        //从MAP中取值
        System.out.println(map.get(1));//通过key取value

        /*Map的遍历*/
        Set<Map.Entry<Integer, String>> entrySet = map.entrySet();
        //方法一 遍历键值对
        for (Map.Entry entry : entrySet) {
            System.out.println(entry.getKey() + "->" + entry.getValue());
        }
        //方法二 遍历键值
        Set<Integer> keys = map.keySet();
        for (Integer integer : keys) {
            String value = map.get(integer);
            System.out.println(integer + "->" + value);
        }
        //方法三 遍历值
        Collection<String> values = map.values();
        for (String value : values) {
            System.out.println(value);
        }
        //方法四 forEach
        //entrySet.forEach(System.out::println);//默认输出方式为：1=大神
        map.forEach((Key, Value) -> System.out.println(Key + "->" + Value));

        //containsKey 是否包含  返回boolean
        System.out.println(map.containsKey(7));

    }

    /**
     * Hashtable
     * public class Hashtable<K,V> extends Dictionary<K,V>
     * implements Map<K,V>, Cloneable, Serializable
     * 此类实现一个哈希表(数组+链表)，该哈希表将键映射到相应的值。任何非null对象都可以用作键或值。
     * 为了成功地在哈希表中存储和获取对象，用作键的对象必须实现hashCode方法和equals方法。
     *
     * 1、默认数组大小为11，加载因子为0.75
     * 2、数组扩充为 原来数组大小*2+1  (oldCapacity << 1) + 1
     * 3、线程安全的，用于多线程访问
     **/
    private static void hashtable() {
        Map<String , String> tables = new Hashtable<>();
        tables.put("one", "Lily");
        tables.put("two", "Tom");
        tables.put("three", "Bin");
        //遍历
        tables.forEach((Key, Value) -> System.out.println(Key +"->" + Value));
    }

    /**
     * LinkedHashMap 是HashMap的子类
     * public class LinkedHashMap<K,V>
     * extends HashMap<K,V> implements Map<K,V>
     * Map接口的哈希表和链接列表实现，具有可预知的迭代顺序。此实现与HashMap的不同之处在于，
     * 后者维护着一个运行于所有条目的双重链接列表。
     * 由于HashMap不能保证顺序恒久不变，此类使用一个双重链表来维护元素添加的顺序
     **/
    private static void linkedHashMap() {
        Map<String, String> links = new LinkedHashMap<>();
        links.put("one", "Lily");
        links.put("two", "Tom");
        links.put("three", "Bin");
        //遍历
        links.forEach((Key, Value) -> System.out.println(Key +"->" + Value));
    }

    /**
     * TreeMap
     * public class TreeMap<K,V> extends AbstractMap<K,V>
     * implements NavigableMap<K,V>, Cloneable, Serializable
     * 基于红黑树(Red-Black tree)的NavigableMap实现。该映射根据其键的自然顺序进行排序，
     * 或者根据创建映射时提供的Comparator进行排序，具体取决于使用的构造方法。
     **/
    private static void treeMap() {
        Map<String, String> treesMap = new TreeMap<>();
        treesMap.put("one", "Lily");
        treesMap.put("two", "Tom");
        treesMap.put("three", "Bin");
        //遍历
        treesMap.forEach((Key, Value) -> System.out.println(Key +"->" + Value));

        Map<Cat, String> treesCat = new TreeMap<>(new CatComparator());
        treesCat.put(new Cat("大神", 30, 1), "Cat1");
        treesCat.put(new Cat("大佬", 25, 2), "Cat2");
        treesCat.put(new Cat("高手", 18, 3), "Cat3");
        //遍历
        treesCat.forEach((Key, Value) -> System.out.println(Key +"->" + Value));

    }




}
